package com.tq.portal.connector.gitlab;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.tq.common.core.util.HttpUtil;
import com.tq.common.core.util.StrUtil;
import com.tq.portal.connector.gitlab.model.GitlabConfig;
import com.tq.portal.connector.gitlab.model.dto.GitLabActivityDTO;
import com.tq.portal.connector.gitlab.model.dto.GitLabCommitDTO;
import com.tq.portal.connector.gitlab.model.dto.GitLabProjectDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * GitLab 客户端实现
 *
 * @author TqCoder
 * @since 2.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GitLabClientImpl implements GitLabClient {

    private static final String PRIVATE_TOKEN = "PRIVATE-TOKEN";
    private static final String API_V4 = "/api/v4";
    
    // 时间格式化器
    private static final DateTimeFormatter INPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final DateTimeFormatter ISO_FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
    private static final ZoneId CHINA_ZONE = ZoneId.of("Asia/Shanghai");
    
    private final GitlabConfig gitlabConfig;

    /**
     * 构建 API URL
     */
    private UriComponentsBuilder buildApiUrl(String path) {
        return UriComponentsBuilder.fromUriString(gitlabConfig.getBaseUrl())
                .path(API_V4)
                .path(path);
    }
    
    /**
     * 将中国时区的时间字符串转换为ISO格式的UTC时间字符串
     * 输入格式: yyyy-MM-dd HH:mm:ss (中国时区)
     * 输出格式: ISO 8601 UTC格式
     */
    private String convertToIsoUtc(String chinaTimeStr) {
        if (StrUtil.isBlank(chinaTimeStr)) {
            return null;
        }
        
        try {
            // 解析中国时区的时间
            LocalDateTime chinaTime = LocalDateTime.parse(chinaTimeStr, INPUT_FORMATTER);
            
            // 转换为UTC时间并格式化为ISO格式
            return chinaTime.atZone(CHINA_ZONE)
                    .withZoneSameInstant(ZoneId.of("UTC"))
                    .format(ISO_FORMATTER);
        } catch (DateTimeParseException e) {
            log.warn("时间格式转换失败: {}, 将直接使用原始值", chinaTimeStr);
            return chinaTimeStr;
        }
    }

    /**
     * 执行 GET 请求
     */
    private HttpResponse executeGet(String url) {
        return HttpUtil.get(url)
                .header(PRIVATE_TOKEN, gitlabConfig.getAccessToken())
                .timeout(gitlabConfig.getTimeout())
                .execute();
    }
    
    /**
     * 执行 POST 请求
     */
    private HttpResponse executePost(String url, String body) {
        return HttpUtil.post(url)
                .header(PRIVATE_TOKEN, gitlabConfig.getAccessToken())
                .header("Content-Type", "application/json")
                .body(body)
                .timeout(gitlabConfig.getTimeout())
                .execute();
    }

    @Override
    public List<GitLabProjectDTO> getAllProjects() {
        log.info("开始获取所有 GitLab 项目");
        
        List<GitLabProjectDTO> allProjects = new ArrayList<>();
        int page = 1;
        int perPage = 100;
        
        while (true) {
            try {
                String url = buildApiUrl("/projects")
                        .queryParam("page", page)
                        .queryParam("per_page", perPage)
                        .queryParam("simple", false)
                        .queryParam("membership", true)
                        .build()
                        .toUriString();
                
                HttpResponse response = executeGet(url);
                
                if (!response.isOk()) {
                    log.error("获取 GitLab 项目失败，状态码: {}, 页码: {}", response.getStatus(), page);
                    break;
                }
                
                String body = response.body();
                JSONArray jsonArray = JSONUtil.parseArray(body);
                
                if (CollUtil.isEmpty(jsonArray)) {
                    break;
                }
                
                List<GitLabProjectDTO> projects = JSONUtil.toList(jsonArray, GitLabProjectDTO.class);
                allProjects.addAll(projects);
                log.info("获取第 {} 页项目，数量: {}", page, projects.size());
                
                if (projects.size() < perPage) {
                    break;
                }
                
                page++;
            } catch (Exception e) {
                log.error("获取 GitLab 项目失败，页码: {}", page, e);
                break;
            }
        }
        
        log.info("获取所有 GitLab 项目完成，总数: {}", allProjects.size());
        return allProjects;
    }

    @Override
    public List<GitLabActivityDTO> getActivities(Integer page, Integer perPage, String after, String before) {
        log.info("开始获取 GitLab 活动列表，页码: {}, 每页数量: {}, 起始时间: {}, 结束时间: {}", 
                page, perPage, after, before);
        
        if (page == null || page < 1) {
            page = 1;
        }
        if (perPage == null || perPage < 1) {
            perPage = 20;
        }
        
        try {
            UriComponentsBuilder urlBuilder = buildApiUrl("/events")
                    .queryParam("page", page)
                    .queryParam("per_page", perPage);
            
            if (StrUtil.isNotBlank(after)) {
                String convertedAfter = convertToIsoUtc(after);
                urlBuilder.queryParam("after", convertedAfter);
            }
            if (StrUtil.isNotBlank(before)) {
                String convertedBefore = convertToIsoUtc(before);
                urlBuilder.queryParam("before", convertedBefore);
            }
            
            String url = urlBuilder.build().toUriString();
            HttpResponse response = executeGet(url);
            
            if (!response.isOk()) {
                log.error("获取 GitLab 活动列表失败，状态码: {}", response.getStatus());
                return new ArrayList<>();
            }
            
            String body = response.body();
            JSONArray jsonArray = JSONUtil.parseArray(body);
            List<GitLabActivityDTO> activities = JSONUtil.toList(jsonArray, GitLabActivityDTO.class);
            
            log.info("获取 GitLab 活动列表成功，数量: {}", CollUtil.isEmpty(activities) ? 0 : activities.size());
            return activities != null ? activities : new ArrayList<>();
        } catch (Exception e) {
            log.error("获取 GitLab 活动列表失败", e);
            return new ArrayList<>();
        }
    }

    @Override
    public List<GitLabCommitDTO> getProjectCommitsWithSubmodules(Integer projectId, String branch, String since, String until) {
        log.info("开始获取项目提交记录，项目ID: {}, 分支: {}", projectId, branch);
        
        List<GitLabCommitDTO> allCommits = new ArrayList<>();
        
        // 1. 获取主项目的提交记录
        List<GitLabCommitDTO> mainCommits = getProjectCommits(projectId, branch, since, until);
        if (CollUtil.isNotEmpty(mainCommits)) {
            // 标记为主项目提交
            GitLabProjectDTO project = getProject(projectId);
            for (GitLabCommitDTO commit : mainCommits) {
                commit.setProjectId(projectId);
                commit.setProjectName(project != null ? project.getName() : "Unknown");
                commit.setIsSubmodule(false);
            }
            allCommits.addAll(mainCommits);
        }
        
        // 2. 获取子模块信息
        List<Map<String, Object>> submodules = getSubmodules(projectId, branch);
        if (CollUtil.isNotEmpty(submodules)) {
            log.info("找到 {} 个子模块", submodules.size());
            
            for (Map<String, Object> submodule : submodules) {
                String submodulePath = (String) submodule.get("path");
                String submoduleUrl = (String) submodule.get("url");
                
                // 尝试从 URL 中提取子模块项目ID
                Integer submoduleProjectId = extractProjectIdFromUrl(submoduleUrl);
                if (submoduleProjectId != null) {
                    log.info("获取子模块提交记录: {}, 项目ID: {}", submodulePath, submoduleProjectId);
                    List<GitLabCommitDTO> submoduleCommits = getProjectCommits(submoduleProjectId, branch, since, until);
                    
                    if (CollUtil.isNotEmpty(submoduleCommits)) {
                        // 标记为子模块提交
                        GitLabProjectDTO subProject = getProject(submoduleProjectId);
                        for (GitLabCommitDTO commit : submoduleCommits) {
                            commit.setProjectId(submoduleProjectId);
                            commit.setProjectName(subProject != null ? subProject.getName() : submodulePath);
                            commit.setIsSubmodule(true);
                        }
                        allCommits.addAll(submoduleCommits);
                    }
                } else {
                    log.warn("无法从 URL 提取子模块项目ID: {}", submoduleUrl);
                }
            }
        }
        
        // 3. 按提交时间排序
        allCommits.sort((c1, c2) -> {
            if (c1.getCommittedDate() == null || c2.getCommittedDate() == null) {
                return 0;
            }
            return c2.getCommittedDate().compareTo(c1.getCommittedDate());
        });
        
        log.info("获取项目提交记录完成，总数: {} (主项目: {}, 子模块: {})", 
                allCommits.size(), 
                mainCommits.size(), 
                allCommits.size() - mainCommits.size());
        
        return allCommits;
    }

    /**
     * 获取项目信息
     */
    private GitLabProjectDTO getProject(Integer projectId) {
        log.debug("获取项目信息，项目ID: {}", projectId);
        
        try {
            String url = buildApiUrl("/projects/{projectId}")
                    .buildAndExpand(projectId)
                    .toUriString();
            HttpResponse response = executeGet(url);
            
            if (!response.isOk()) {
                log.error("获取项目信息失败，状态码: {}, 项目ID: {}", response.getStatus(), projectId);
                return null;
            }
            
            String body = response.body();
            JSONObject jsonObject = JSONUtil.parseObj(body);
            return JSONUtil.toBean(jsonObject, GitLabProjectDTO.class);
        } catch (Exception e) {
            log.error("获取项目信息失败，项目ID: {}", projectId, e);
            return null;
        }
    }

    /**
     * 获取项目的提交记录
     */
    private List<GitLabCommitDTO> getProjectCommits(Integer projectId, String branch, String since, String until) {
        List<GitLabCommitDTO> allCommits = new ArrayList<>();
        int page = 1;
        int perPage = 100;
        
        // 如果没有指定分支，使用默认分支
        if (StrUtil.isBlank(branch)) {
            GitLabProjectDTO project = getProject(projectId);
            branch = project != null && StrUtil.isNotBlank(project.getDefaultBranch()) 
                    ? project.getDefaultBranch() 
                    : "master";
        }
        
        while (true) {
            try {
                UriComponentsBuilder urlBuilder = buildApiUrl("/projects/{projectId}/repository/commits")
                        .queryParam("page", page)
                        .queryParam("per_page", perPage)
                        .queryParam("ref_name", branch);
                
                if (StrUtil.isNotBlank(since)) {
                    String convertedSince = convertToIsoUtc(since);
                    urlBuilder.queryParam("since", convertedSince);
                }
                if (StrUtil.isNotBlank(until)) {
                    String convertedUntil = convertToIsoUtc(until);
                    urlBuilder.queryParam("until", convertedUntil);
                }
                
                String url = urlBuilder.buildAndExpand(projectId).toUriString();
                HttpResponse response = executeGet(url);
                
                if (!response.isOk()) {
                    log.error("获取项目提交记录失败，状态码: {}, 项目ID: {}, 页码: {}", response.getStatus(), projectId, page);
                    break;
                }
                
                String body = response.body();
                JSONArray jsonArray = JSONUtil.parseArray(body);
                
                if (CollUtil.isEmpty(jsonArray)) {
                    break;
                }
                
                List<GitLabCommitDTO> commits = JSONUtil.toList(jsonArray, GitLabCommitDTO.class);
                allCommits.addAll(commits);
                log.debug("获取项目 {} 第 {} 页提交记录，数量: {}", projectId, page, commits.size());
                
                if (commits.size() < perPage) {
                    break;
                }
                
                page++;
            } catch (Exception e) {
                log.error("获取项目提交记录失败，项目ID: {}, 页码: {}", projectId, page, e);
                break;
            }
        }
        
        return allCommits;
    }

    /**
     * 获取项目的子模块信息
     */
    private List<Map<String, Object>> getSubmodules(Integer projectId, String branch) {
        if (StrUtil.isBlank(branch)) {
            GitLabProjectDTO project = getProject(projectId);
            branch = project != null && StrUtil.isNotBlank(project.getDefaultBranch()) 
                    ? project.getDefaultBranch() 
                    : "master";
        }
        
        try {
            // 尝试获取 .gitmodules 文件内容
            String url = buildApiUrl("/projects/{projectId}/repository/files/{filePath}")
                    .queryParam("ref", branch)
                    .buildAndExpand(projectId, ".gitmodules")
                    .toUriString();
            
            HttpResponse response = executeGet(url);
            
            if (!response.isOk()) {
                log.debug("项目 {} 没有子模块文件", projectId);
                return new ArrayList<>();
            }
            
            String body = response.body();
            JSONObject jsonObject = JSONUtil.parseObj(body);
            
            if (jsonObject.containsKey("content")) {
                String content = jsonObject.getStr("content");
                // GitLab API 返回的内容是 base64 编码的
                String decodedContent = Base64.decodeStr(content);
                return parseGitmodulesContent(decodedContent);
            }
        } catch (Exception e) {
            log.debug("项目 {} 没有子模块或获取子模块信息失败", projectId);
        }
        
        return new ArrayList<>();
    }

    /**
     * 解析 .gitmodules 文件内容
     */
    private List<Map<String, Object>> parseGitmodulesContent(String content) {
        List<Map<String, Object>> submodules = new ArrayList<>();
        
        String[] lines = content.split("\n");
        Map<String, Object> currentSubmodule = null;
        
        for (String line : lines) {
            line = line.trim();
            
            if (line.startsWith("[submodule")) {
                if (currentSubmodule != null) {
                    submodules.add(currentSubmodule);
                }
                currentSubmodule = new HashMap<>();
            } else if (currentSubmodule != null && line.contains("=")) {
                String[] parts = line.split("=", 2);
                String key = parts[0].trim();
                String value = parts.length > 1 ? parts[1].trim() : "";
                currentSubmodule.put(key, value);
            }
        }
        
        if (currentSubmodule != null) {
            submodules.add(currentSubmodule);
        }
        
        return submodules;
    }

    /**
     * 从 URL 中提取项目ID
     * 支持的格式：
     * - http://gitlab.example.com/group/project.git
     * - http://gitlab.example.com/group/project
     */
    private Integer extractProjectIdFromUrl(String url) {
        if (StrUtil.isBlank(url)) {
            return null;
        }
        
        try {
            // 移除 .git 后缀
            String cleanUrl = url.endsWith(".git") ? url.substring(0, url.length() - 4) : url;
            
            // 提取项目路径
            String baseUrl = gitlabConfig.getBaseUrl();
            if (cleanUrl.startsWith(baseUrl)) {
                String projectPath = cleanUrl.substring(baseUrl.length());
                if (projectPath.startsWith("/")) {
                    projectPath = projectPath.substring(1);
                }
                
                // 通过项目路径查找项目ID
                return getProjectIdByPath(projectPath);
            }
        } catch (Exception e) {
            log.error("从 URL 提取项目ID失败: {}", url, e);
        }
        
        return null;
    }

    /**
     * 通过项目路径获取项目ID
     */
    private Integer getProjectIdByPath(String projectPath) {
        try {
            String url = buildApiUrl("/projects/{projectPath}")
                    .buildAndExpand(projectPath)
                    .toUriString();
            
            HttpResponse response = executeGet(url);
            
            if (!response.isOk()) {
                log.error("通过路径获取项目ID失败，状态码: {}, 项目路径: {}", response.getStatus(), projectPath);
                return null;
            }
            
            String body = response.body();
            JSONObject jsonObject = JSONUtil.parseObj(body);
            GitLabProjectDTO project = JSONUtil.toBean(jsonObject, GitLabProjectDTO.class);
            
            return project != null ? project.getId() : null;
        } catch (Exception e) {
            log.error("通过路径获取项目ID失败: {}", projectPath, e);
            return null;
        }
    }
}

